import java.io.InputStream;
import java.io.OutputStream;
import java.rmi.UnmarshalException;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.thrift.TDeserializer;
import org.apache.thrift.TSerializer;
import org.apache.thrift.protocol.TBinaryProtocol;

import data.CompoundContainer;

import thrift.TBool;
import thrift.TBools;
import thrift.TByte;
import thrift.TBytes;
import thrift.TDouble;
import thrift.TDoubles;
import thrift.TInt16;
import thrift.TInt16s;
import thrift.TInt32;
import thrift.TInt32s;
import thrift.TInt64;
import thrift.TInt64s;
import thrift.TString;
import thrift.TStrings;


public class ThriftTest extends SerializationTest {

	public ThriftTest(TestRecorder recorder, Storage storage) {
		super(recorder, storage);
		super.technique = "\"Thrift Binary\"";
	}

	protected static TSerializer serializer = new TSerializer(new TBinaryProtocol.Factory());
	protected static TDeserializer deserializer = new TDeserializer(new TBinaryProtocol.Factory());


	@Override
	protected void testBoolWrite(boolean value, OutputStream os) throws Exception {
		TBool type = new TBool(value);
		write(serializer.serialize(type), os);
	}
	
	@Override
	protected void testBoolWrite(boolean[] value, OutputStream os) throws Exception {
		TBools type = new TBools();
		for (boolean i : value) {
			type.addToBoolType(i);
		}
		write(serializer.serialize(type), os);
	}

	@Override
	protected boolean testBoolRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TBool type = new TBool();
		deserializer.deserialize(type, content);
		return type.boolType;
	}

	@Override
	protected boolean[] testBoolsRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TBools type = new TBools();
		deserializer.deserialize(type, content);
		return ArrayUtils.toPrimitive(type.getBoolType().toArray(new Boolean[type.getBoolType().size()]));
	}

	
	@Override
	protected void testByteWrite(byte value, OutputStream os) throws Exception {
		TByte type = new TByte(value);
		write(serializer.serialize(type), os);
	}

	@Override
	protected void testByteWrite(byte[] value, OutputStream os) throws Exception {
		TBytes type = new TBytes();
		for (byte i : value) {
			type.addToByteType(i);
		}
		write(serializer.serialize(type), os);
	}

	@Override
	protected byte testByteRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TByte type = new TByte();
		deserializer.deserialize(type, content);
		return type.byteType;
	}

	@Override
	protected byte[] testBytesRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TBytes type = new TBytes();
		deserializer.deserialize(type, content);
		return ArrayUtils.toPrimitive(type.getByteType().toArray(new Byte[type.getByteType().size()]));
	}

	
	@Override
	protected void testShortWrite(short value, OutputStream os) throws Exception {
		TInt16 type = new TInt16(value);
		write(serializer.serialize(type), os);
	}
	
	@Override
	protected void testShortWrite(short[] value, OutputStream os) throws Exception {
		TInt16s type = new TInt16s();
		for (short i : value) {
			type.addToInt16Type(i);
		}
		write(serializer.serialize(type), os);
	}

	@Override
	protected short testShortRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TInt16 type = new TInt16();
		deserializer.deserialize(type, content);
		return type.int16Type;
	}

	@Override
	protected short[] testShortsRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TInt16s type = new TInt16s();
		deserializer.deserialize(type, content);
		return ArrayUtils.toPrimitive(type.getInt16Type().toArray(new Short[type.getInt16Type().size()]));
	}

	
	@Override
	protected void testIntWrite(int value, OutputStream os) throws Exception {
		TInt32 type = new TInt32(value);
		write(serializer.serialize(type), os);
	}
	
	@Override
	protected void testIntWrite(int[] value, OutputStream os) throws Exception {
		TInt32s type = new TInt32s();
		for (int i : value) {
			type.addToInt32Type(i);
		}
		write(serializer.serialize(type), os);
	}

	@Override
	protected int testIntRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TInt32 type = new TInt32();
		deserializer.deserialize(type, content);
		return type.int32Type;
	}

	@Override
	protected int[] testIntsRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TInt32s type = new TInt32s();
		deserializer.deserialize(type, content);
		return ArrayUtils.toPrimitive(type.getInt32Type().toArray(new Integer[type.getInt32Type().size()]));
	}

	@Override
	protected void testLongWrite(long value, OutputStream os) throws Exception {
		TInt64 type = new TInt64(value);
		write(serializer.serialize(type), os);
	}
	
	@Override
	protected void testLongWrite(long[] value, OutputStream os) throws Exception {
		TInt64s type = new TInt64s();
		for (long i : value) {
			type.addToInt64Type(i);
		}
		write(serializer.serialize(type), os);
	}

	@Override
	protected long testLongRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TInt64 type = new TInt64();
		deserializer.deserialize(type, content);
		return type.int64Type;
	}

	@Override
	protected long[] testLongsRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TInt64s type = new TInt64s();
		deserializer.deserialize(type, content);
		return ArrayUtils.toPrimitive(type.getInt64Type().toArray(new Long[type.getInt64Type().size()]));
	}

	@Override
	protected void testFloatWrite(float value, OutputStream os) throws Exception {
		testDoubleWrite(value, os);
	}

	@Override
	protected void testFloatWrite(float[] value, OutputStream os) throws Exception {
		double[] doubles = new double[value.length];
		for (int i = 0; i < value.length; i++) {
			doubles[i] = value[i];
		}
		testDoubleWrite(doubles, os);
	}

	@Override
	protected float testFloatRead(InputStream is) throws Exception {
		return (float) testDoubleRead(is);
	}

	@Override
	protected float[] testFloatsRead(InputStream is) throws Exception {
		double[] doubles = testDoublesRead(is);
		float[] floats = new float[doubles.length];
		for (int i = 0; i < doubles.length; i++) {
			floats[i] = (float) doubles[i];
		}
		return floats;
	}

	@Override
	protected void testDoubleWrite(double value, OutputStream os) throws Exception {
		TDouble type = new TDouble(value);
		write(serializer.serialize(type), os);
	}
	
	@Override
	protected void testDoubleWrite(double[] value, OutputStream os) throws Exception {
		TDoubles type = new TDoubles();
		for (double i : value) {
			type.addToDoubleType(i);
		}
		write(serializer.serialize(type), os);
	}

	@Override
	protected double testDoubleRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TDouble type = new TDouble();
		deserializer.deserialize(type, content);
		return type.doubleType;
	}

	@Override
	protected double[] testDoublesRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TDoubles type = new TDoubles();
		deserializer.deserialize(type, content);
		return ArrayUtils.toPrimitive(type.getDoubleType().toArray(new Double[type.getDoubleType().size()]));
	}

	
	@Override
	protected void testStringWrite(String value, OutputStream os) throws Exception {
		TString type = new TString(value);
		write(serializer.serialize(type), os);
	}
	
	@Override
	protected void testStringWrite(String[] value, OutputStream os) throws Exception {
		TStrings type = new TStrings();
		for (String i : value) {
			type.addToStringType(i);
		}
		write(serializer.serialize(type), os);
	}

	@Override
	protected String testStringRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TString type = new TString();
		deserializer.deserialize(type, content);
		return type.stringType;
	}

	@Override
	protected String[] testStringsRead(InputStream is) throws Exception {
		byte[] content = read(is);
		TStrings type = new TStrings();
		deserializer.deserialize(type, content);
		String[] strings = new String[iterations];
		for (int i = 0; i < type.getStringTypeSize(); i++) {
			strings[i] = type.getStringType().get(i);
		}
		return strings;
	}

	TCompound transformCompound(CompoundContainer orig) {
		TCompound comp = new TCompound();
		comp.boolType = orig.booleanvalue;
		comp.byteType = orig.bytevalue;
		comp.shortType = orig.shortvalue;
		comp.intType = orig.intvalue;
		comp.longType = orig.longvalue;
		comp.floatType = orig.floatvalue;
		comp.doubleType = orig.doublevalue;
		comp.stringType = orig.stringvalue;
		
		if (orig.compoundvalue != null) {
			TCompoundInner1 compInner = new TCompoundInner1();
			compInner.boolType = orig.compoundvalue.booleanvalue;
			compInner.byteType = orig.compoundvalue.bytevalue;
			compInner.shortType = orig.compoundvalue.shortvalue;
			compInner.intType = orig.compoundvalue.intvalue;
			compInner.longType = orig.compoundvalue.longvalue;
			compInner.floatType = orig.compoundvalue.floatvalue;
			compInner.doubleType = orig.compoundvalue.doublevalue;
			compInner.stringType = orig.compoundvalue.stringvalue;
			comp.compoundType = compInner;
		}
		
		if (orig.compoundvalue.compoundvalue != null) {
			TCompoundInner2 compInner = new TCompoundInner2();
			compInner.boolType = orig.compoundvalue.compoundvalue.booleanvalue;
			compInner.byteType = orig.compoundvalue.compoundvalue.bytevalue;
			compInner.shortType = orig.compoundvalue.compoundvalue.shortvalue;
			compInner.intType = orig.compoundvalue.compoundvalue.intvalue;
			compInner.longType = orig.compoundvalue.compoundvalue.longvalue;
			compInner.floatType = orig.compoundvalue.compoundvalue.floatvalue;
			compInner.doubleType = orig.compoundvalue.compoundvalue.doublevalue;
			compInner.stringType = orig.compoundvalue.compoundvalue.stringvalue;
			comp.compoundType.compoundType = compInner;
		}	
		return comp;
	}
	
	CompoundContainer transformCompound(TCompound orig) {
		CompoundContainer comp = new CompoundContainer();
		comp.booleanvalue = orig.boolType;
		comp.bytevalue = orig.byteType;
		comp.shortvalue = orig.shortType;
		comp.intvalue = orig.intType;
		comp.longvalue = orig.longType;
		comp.floatvalue = (float) orig.floatType;
		comp.doublevalue = orig.doubleType;
		comp.stringvalue = orig.stringType;
		
		if (orig.compoundType != null) {
			comp.compoundvalue = new CompoundContainer();
			comp.compoundvalue.booleanvalue = orig.compoundType .boolType;
			comp.compoundvalue.bytevalue = orig.compoundType .byteType;
			comp.compoundvalue.shortvalue = orig.compoundType .shortType;
			comp.compoundvalue.intvalue = orig.compoundType .intType;
			comp.compoundvalue.longvalue = orig.compoundType .longType;
			comp.compoundvalue.floatvalue = (float) orig.compoundType .floatType;
			comp.compoundvalue.doublevalue = orig.compoundType .doubleType;
			comp.compoundvalue.stringvalue = orig.compoundType .stringType;
		}
		
		if (orig.compoundType.compoundType != null) {
			comp.compoundvalue.compoundvalue = new CompoundContainer();
			comp.compoundvalue.compoundvalue.booleanvalue = orig.compoundType.compoundType.boolType;
			comp.compoundvalue.compoundvalue.bytevalue = orig.compoundType.compoundType.byteType;
			comp.compoundvalue.compoundvalue.shortvalue = orig.compoundType.compoundType.shortType;
			comp.compoundvalue.compoundvalue.intvalue = orig.compoundType.compoundType.intType;
			comp.compoundvalue.compoundvalue.longvalue = orig.compoundType.compoundType.longType;
			comp.compoundvalue.compoundvalue.floatvalue = (float) orig.compoundType.compoundType.floatType;
			comp.compoundvalue.compoundvalue.doublevalue = orig.compoundType.compoundType.doubleType;
			comp.compoundvalue.compoundvalue.stringvalue = orig.compoundType.compoundType.stringType;
		}

		return comp;
	}
	
	@Override
	protected void testCompoundWrite(CompoundContainer value, OutputStream os)
			throws Exception {
		TCompound type = transformCompound(value);
		write(serializer.serialize(type), os);
		
	}

	@Override
	protected void testCompoundWrite(CompoundContainer[] value, OutputStream os)
			throws Exception {
		TCompounds type = new TCompounds();
		for (CompoundContainer i : value) {
			type.addToCompoundType(transformCompound(i));
		}
		write(serializer.serialize(type), os);
	}

	@Override
	protected CompoundContainer testCompoundRead(InputStream is)
			throws Exception {
		byte[] content = read(is);
		TCompound type = new TCompound();
		deserializer.deserialize(type, content);
		return transformCompound(type);
	}

	@Override
	protected CompoundContainer[] testCompoundsRead(InputStream is)
			throws Exception {
		byte[] content = read(is);
		TCompounds type = new TCompounds();
		deserializer.deserialize(type, content);
		CompoundContainer[] comps = new CompoundContainer[iterations];
		for (int i = 0; i < type.getCompoundTypeSize(); i++) {
			comps[i] = transformCompound(type.getCompoundType().get(i));
		}
		return comps;
	}

}
